package com.yunxi.module.system.service.social.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.Assert;
import com.xingyuv.jushauth.model.AuthUser;
import com.yunxi.framework.common.util.collection.CollectionUtils;
import com.yunxi.module.system.dto.social.SocialUserBindReqDTO;
import com.yunxi.module.system.mapper.social.SocialUserBindMapper;
import com.yunxi.module.system.mapper.social.SocialUserMapper;
import com.yunxi.module.system.model.social.SocialUserBindDO;
import com.yunxi.module.system.model.social.SocialUserDO;
import com.yunxi.module.system.service.social.SocialClientService;
import com.yunxi.module.system.service.social.SocialUserService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;

import javax.annotation.Resource;
import javax.validation.constraints.NotNull;
import java.util.Collections;
import java.util.List;

import static com.yunxi.framework.common.util.json.JsonUtils.toJsonString;

/**
 * <p>
 * 社交用户 Service 接口实现类，例如说社交平台的授权登录
 * </p>
 *
 * @author lidy
 * @since 2024-05-08
 */
@Service
@Validated
@Slf4j
public class SocialUserServiceImpl implements SocialUserService {

    @Resource
    private SocialUserMapper socialUserMapper;

    @Resource
    private SocialClientService socialClientService;

    @Resource
    private SocialUserBindMapper socialUserBindMapper;

    /**
     * 绑定社交用户
     *
     * @param reqDTO 绑定信息
     * @return 社交用户 openid
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public String bindSocialUser(SocialUserBindReqDTO reqDTO) {
        // 获得社交用户
        SocialUserDO socialUserDO = authSocialUser(reqDTO.getSocialType(), reqDTO.getUserType(), reqDTO.getCode(), reqDTO.getState());
        Assert.notNull(socialUserDO, "社交用户不能为空");

        // 社交用户可能之前绑定过别的用户，需要进行解绑
        socialUserBindMapper.deleteByUserTypeAndSocialUserId(reqDTO.getUserType(), socialUserDO.getId());

        // 用户可能之前已经绑定过该社交类型，需要进行解绑
        socialUserBindMapper.deleteByUserTypeAndUserIdAndSocialType(reqDTO.getUserType(), reqDTO.getUserId(), socialUserDO.getType());

        // 绑定当前登录的社交用户
        SocialUserBindDO userBindDO = SocialUserBindDO.builder()
                .userId(reqDTO.getUserId()).userType(reqDTO.getUserType())
                .socialUserId(socialUserDO.getId()).socialType(socialUserDO.getType()).build();
        socialUserBindMapper.insert(userBindDO);
        return socialUserDO.getOpenid();
    }

    /**
     * 获得制定用户的社交用户列表
     *
     * @param userId   用户编号
     * @param userType 用户类型
     * @return 社交用户列表
     */
    @Override
    public List<SocialUserDO> getSocialUserList(Long userId, Integer userType) {
        // 获得绑定
        List<SocialUserBindDO> socialUserBindDOS = socialUserBindMapper.selectListByUserIdAndUserType(userId, userType);

        if (CollUtil.isEmpty(socialUserBindDOS)) {
            return Collections.emptyList();
        }

        // 获得社交用户
        return socialUserMapper.selectBatchIds(CollectionUtils.convertList(socialUserBindDOS, SocialUserBindDO::getSocialUserId));
    }

    /**
     * 授权获得对应的社交用户
     * 如果授权失败，则会抛出 {@link com.yunxi.system.enums.social.SocialTypeEnum}
     *
     * @param socialType 社交平台类型 {@link com.yunxi.system.enums.social.SocialTypeEnum}
     * @param userType   用户类型
     * @param code       授权码
     * @param state      state
     * @return 授权用户
     */
    @NotNull
    private SocialUserDO authSocialUser(Integer socialType, Integer userType, String code, String state) {
        // 优先从数据库中获取，因为 code 有且可以使用一次
        // 在社交登录时，当未绑定 User 时，需要绑定登录，此时需要 code 使用两次
        SocialUserDO socialUserDO = socialUserMapper.selectByTypeAndCodeAnState(socialType, code, state);
        if (socialUserDO != null) {
            return socialUserDO;
        }

        // 请求获取
        AuthUser authUser = socialClientService.getAuthUser(socialType, userType, code, state);
        Assert.notNull(authUser, "三方用户不能为空");

        // 保存到数据库中
        socialUserDO = socialUserMapper.selectByTypeAndOpenid(socialType, authUser.getUuid());
        if (socialUserDO == null) {
            socialUserDO = new SocialUserDO();
        }

        socialUserDO.setType(socialType);
        socialUserDO.setCode(code);
        socialUserDO.setState(state);// 需要保存 code + state 字段，保证后面的查询
        socialUserDO.setOpenid(authUser.getUuid());
        socialUserDO.setToken(authUser.getToken().getAccessToken());
        socialUserDO.setRawTokenInfo(toJsonString(authUser.getToken()));
        socialUserDO.setNickname(authUser.getNickname());
        socialUserDO.setAvatar(authUser.getAvatar());
        socialUserDO.setRawUserInfo(toJsonString(authUser.getRawUserInfo()));

        if (socialUserDO.getId() == null) {
            socialUserMapper.insert(socialUserDO);
        } else {
            socialUserMapper.updateById(socialUserDO);
        }

        return socialUserDO;
    }
}
